package com.geodroid.common.kernel.geos;



import java.util.ArrayList;

import com.geodroid.common.kernel.Kernel;
import com.geodroid.common.kernel.Path;
import com.geodroid.common.kernel.PathParameter;
import com.geodroid.kernel.matrix.Coords;

public class GeoPoint2 extends GeoVec3D {

	
	private boolean isInfinite, isDefined;
	
	/** inhomogeneous x-coord */
	public double inhomX;
	/** inhomogeneous y-coord*/
	public double inhomY;

	private Path path;
	

	// for identifying incidence by construction
	// case by case.
	// currently implemented for
	// lines: line by two point, intersect lines, line/conic, point on line
	// TODO: parallel line, perpenticular line
	private ArrayList<GeoElement> incidenceList;

	private PathParameter pathParameter;
	/**
	 * @return list of objects incident by construction
	 */
	public ArrayList<GeoElement> getIncidenceList() {
		return incidenceList;
	}
	/**
	 * @param list list of objects incident by construction
	 */
	public void setIncidenceList(ArrayList<GeoElement> list) {
		incidenceList = new ArrayList<GeoElement>(list);
	}

	/**
	 * initialize incidenceList, and add the point itself to the list as the
	 * first element.
	 */
	public void createIncidenceList() {
		incidenceList = new ArrayList<GeoElement>();
		incidenceList.add(this);
	}
	
	/**
	 * add geo to incidenceList of this, and also add this to pointsOnConic
	 * (when geo is a conic) or to pointsOnLine (when geo is a line)
	 * 
	 * @param geo incident object
	 */
	public void addIncidence(GeoElement geo) {
		if (incidenceList == null)
			createIncidenceList();
		if (!incidenceList.contains(geo))
			incidenceList.add(geo);

		// GeoConicND, GeoLine, GeoPoint are the three types who have an
		// incidence list
		if (geo.isGeoConic()){
//			((GeoConicND) geo).addPointOnConic(this);// GeoConicND
		}
		else if (geo.isGeoLine())
			((GeoLine) geo).addPointOnLine(this);
		// TODO: if geo instanceof GeoPoint...
	}
	
	
	/**
	 * @param geo incident geo tobe removed
	 */
	public final void removeIncidence(GeoElement geo) {
		if (incidenceList != null)
			incidenceList.remove(geo);

		if (geo.isGeoConic()){
//			((GeoConicND) geo).removePointOnConic(this);
		}
		else if (geo.isGeoLine())
			((GeoLine) geo).removePointOnLine(this);
		// TODO: if geo instanceof GeoPoint...
	}
	
	/**
	 * @return the inhomX
	 */
	public double getInhomX() {
		return inhomX;
	}

	/**
	 * @param inhomX the inhomX to set
	 */
	public void setInhomX(double inhomX) {
		this.inhomX = inhomX;
	}

	/**
	 * @return the inhomY
	 */
	public double getInhomY() {
		return inhomY;
	}

	/**
	 * @param inhomY the inhomY to set
	 */
	public void setInhomY(double inhomY) {
		this.inhomY = inhomY;
	}

	/**
	 * @param isInfinite the isInfinite to set
	 */
	public void setInfinite(boolean isInfinite) {
		this.isInfinite = isInfinite;
	}

	/**
	 * @param isDefined the isDefined to set
	 */
	public void setDefined(boolean isDefined) {
		this.isDefined = isDefined;
	}

	public GeoPoint2(double x, double y, double z) {
		super(x, y, z);
		updateCoords() ;
	}
	
	public GeoPoint2(double x, double y) {
		super(x, y, 1);
		updateCoords() ;
	}


	public GeoPoint2() {
		super(0, 0, 1);
		updateCoords() ;
	}

	@Override
	public void setCoords(double x, double y, double z) {
		// set coordinates
		this.x = x;
		this.y = y;
		this.z = z;
		updateCoords() ; 
	}
	
	public void setCoords(double x, double y) {
		// set coordinates
		this.x = x;
		this.y = y;
		this.z = 1;
		updateCoords() ; 
	}
	

	@Override
	public void setCoords(GeoVec3D v) {
		// set coordinates
		this.x = v.x;
		this.y = v.y;
		this.z = v.z;
		updateCoords() ; 
	}

	final public void updateCoords() {
		// infinite point
		if (Kernel.isZero(z)) {
			isInfinite = true;
			isDefined = !(Double.isNaN(x) || Double.isNaN(y));
			inhomX = Double.NaN;
			inhomY = Double.NaN;
		}
		// finite point
		else {
			isInfinite = false;
			isDefined = !(Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z));

			if (isDefined) {
				// make sure the z coordinate is always positive
				// this is important for the orientation of a line or ray
				// computed using two points P, Q with cross(P, Q)
				if (z < 0) {
					x = -x;
					y = -y;
					z = -z;
				}

				// update inhomogeneous coords
				if (z == 1.0) {
					inhomX = x;
					inhomY = y;
				} else {
					inhomX = x / z;
					inhomY = y / z;
				}
			} else {
				inhomX = Double.NaN;
				inhomY = Double.NaN;
			}
		}
	}
	
	final public boolean isInfinite() {
		return isInfinite;
	}

	public boolean isDefined() {
		// TODO Auto-generated method stub
		return isDefined;
	}

	public Path getPath() {
		// TODO Auto-generated method stub
		return path ;
	}
	/**
	 * Sets path parameter to null
	 */
	final public void clearPathParameter() {
		pathParameter = null;
	}

	final public PathParameter getPathParameter() {
		if (pathParameter == null)
			pathParameter = new PathParameter(0);
		return pathParameter;
	}
	
	public Coords getCoordsInD(int dimension) {
		switch (dimension) {
		case 2:
			return new Coords(new double[] { x, y, z });
		case 3:
			// Application.debug(getLabel()+": "+x+","+y+","+z);
			return new Coords(x, y, 0, z);
		default:
			return null;
		}
	}
	
	public void setCoords2D(double x, double y, double z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}
	@Override
	public void setUndefined() {
		isDefined = false;
		super.setUndefined();
	}
	
//	// only used for 3D stuff
//	public void updateCoordsFrom2D(boolean doPathOrRegion, CoordSys coordsys) {
//		//3D only
//	}
}
